﻿using BaseTool;
using OpenCvSharp;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;

namespace Airthmetic
{
    /// <summary>
    /// 条纹光栅工具
    /// </summary>
    public class StripedGratingImageTool
    {
        /// <summary> 条纹光栅参数 </summary>
        [XmlElement]
        private StripedGratingImageParam Param = new StripedGratingImageParam();

        /// <summary> 条纹光栅序列 </summary>
        public List<PatternData> PatternList = new List<PatternData>();
        /// <summary> 格雷码参数 </summary>
        public GrayCodeParam GrayCode { get { return Param.GrayCode; } set { Param.GrayCode = value; } }
        /// <summary> 相移法参数 </summary>
        public PhaseShiftParam PhaseShift { get { return Param.PhaseShift; } set { Param.PhaseShift = value; } }

        #region 初始化、参数读取\写入
        /// <summary>
        /// 构造
        /// </summary>
        public StripedGratingImageTool() { }

        /// <summary>
        /// 读取
        /// </summary>
        public void Read()
        {
            try
            {
                if (!File.Exists(PathTool.StripedGratingImageXml))
                {
                    Param = new StripedGratingImageParam();
                    Save();
                }
                Param = (StripedGratingImageParam)FileTool.ReadXML(PathTool.StripedGratingImageXml, typeof(StripedGratingImageParam));
                Log.SaveLog($"读取条纹光栅工具参数成功", LogType.Run, Color.Black);
            }
            catch (Exception ex)
            {
                Log.SaveError(ex, $"读取条纹光栅工具参数异常，{ex.Message}");
            }
        }
        /// <summary>
        /// 写入
        /// </summary>
        public bool Save()
        {
            try
            {
                bool isok = FileTool.WriteXML(PathTool.StripedGratingImageXml, Param);
                Log.SaveLog($"保存条纹光栅工具参数{(isok ? "成功" : "失败")}", LogType.Run, isok ? Color.Black : Color.Red);
                return isok;
            }
            catch (Exception ex)
            {
                Log.SaveError(ex, $"保存条纹光栅工具参数异常，{ex.Message}");
                return false;
            }
        }
        #endregion

        #region 方法
        /// <summary>
        /// 计算格雷码最佳深度
        /// </summary>
        /// <param name="num"></param>
        /// <returns></returns>
        public static int GetDepth(int num)
        {
            int depth = 0;
            for (int i = (1 << depth); i < num; i = (1 << depth))
            {
                depth++;
            }
            return depth;
        }
        /// <summary>
        /// 生成格雷码序列光栅
        /// </summary>
        /// <param name="param"> 格雷码参数 </param>
        /// <returns></returns>
        public void GenerateGrayCodeImages(GrayCodeParam param)
        {
            // patterns
            // -----------
            // 00 white
            // 01 black
            // -----------
            // 02 vertical, bit N-0, normal
            // 03 vertical, bit N-0, inverted
            // 04 vertical, bit N-1, normal
            // 04 vertical, bit N-2, inverted
            // ..
            // XX =  (2*_pattern_count + 2) - 2 vertical, bit N, normal
            // XX =  (2*_pattern_count + 2) - 1 vertical, bit N, inverted
            // -----------
            // 2+N+00 = 2*(_pattern_count + 2) horizontal, bit N-0, normal
            // 2+N+01 horizontal, bit N-0, inverted
            // ..
            // YY =  (4*_pattern_count + 2) - 2 horizontal, bit N, normal
            // YY =  (4*_pattern_count + 2) - 1 horizontal, bit N, inverted

            int cols = param.ImageWidth;
            int rows = param.ImageHeight;

            int vbits = param.VerticalDepth + 1;
            int hbits = param.HorizontalDepth + 1;

            //white or black
            int vmask = 0;
            int hmask = 0;
            int voffset = ((1 << vbits) - cols) / 2;
            int hoffset = ((1 << hbits) - rows) / 2;

            Mat normal = new Mat();
            Mat inverted = new Mat();

            if (param.IsAllWhiteAndBlack)
            {
                MakePattern(1, cols, vmask, voffset, hmask, hoffset, out normal, out inverted);
                PatternList.Add(new PatternData(inverted.Clone(), true, cols, rows));
                PatternList.Add(new PatternData(normal.Clone(), true, cols, rows));
            }

            //vertical
            vmask = 0;
            hmask = 0;

            for (int b = 1; b < vbits; b++)
            {
                vmask = 1 << (vbits - b);
                MakePattern(1, cols, vmask, voffset, hmask, hoffset, out normal, out inverted);
                PatternList.Add(new PatternData(normal.Clone(), true, cols, rows));
                if (param.IsComplementaryGrayCode)
                    PatternList.Add(new PatternData(inverted.Clone(), true, cols, rows));
            }

            //horizontal
            vmask = 0;
            hmask = 0;

            for (int b = 1; b < hbits; b++)
            {
                hmask = 1 << (hbits - b);
                MakePattern(rows, 1, vmask, voffset, hmask, hoffset, out normal, out inverted);
                PatternList.Add(new PatternData(normal.Clone(), true, cols, rows));
                if (param.IsComplementaryGrayCode)
                    PatternList.Add(new PatternData(inverted.Clone(), true, cols, rows));
            }
        }
        /// <summary>
        /// 生成格雷码序列光栅
        /// </summary>
        private void MakePattern(int rows, int cols, int vmask, int voffset, int hmask, int hoffset, out Mat normal, out Mat inverted)
        {
            normal = new Mat(rows, cols, MatType.CV_8UC1);
            inverted = new Mat(rows, cols, MatType.CV_8UC1);

            byte black = 0;
            byte white = 255;

            for (int h = 0; h < rows; h++)
            {
                for (int w = 0; w < cols; w++)
                {
                    int test = (ImageTool.BinaryToGray(h, hoffset) & hmask) +
                        (ImageTool.BinaryToGray(w, voffset) & vmask);

                    if (test == 0)
                    {
                        normal.Set(h, w, black);
                        inverted.Set(h, w, white);
                    }
                    else
                    {
                        normal.Set(h, w, white);
                        inverted.Set(h, w, black);
                    }
                }
            }
        }

        /// <summary>
        /// 生成相移法序列光栅
        /// </summary>
        /// <param name="param">相移法参数</param>
        /// <returns></returns>
        public void GeneratePhaseShiftPatterns(PhaseShiftParam param)
        {
            int A = param.A;
            int B = param.B;
            double T = param.T;
            int N = param.N;
            int width = param.ImageWidth;
            int height = param.ImageHeight;
            double f_2pi = 2.0 * Math.PI / T;

            for (int k = 0; k < N; k++)
            {
                if (param.IsVertical)
                {
                    Mat mat = new Mat(1, width, MatType.CV_8UC1);

                    for (int w = 0; w < width; w++)
                    {
                        double value = (A + B * Math.Cos(f_2pi * w + 2.0 * Math.PI * k / N));
                        mat.Set(0, w, (byte)value);
                    }
                    PatternList.Add(new PatternData(mat, false, width, height));
                }
                else
                {
                    Mat mat = new Mat(height, 1, MatType.CV_8UC1);

                    for (int h = 0; h < height; h++)
                    {
                        double value = (A + B * Math.Cos(f_2pi * h + 2.0 * Math.PI * k / N));
                        mat.Set(h, 0, (byte)value);
                    }
                    PatternList.Add(new PatternData(mat, false, width, height));
                }
            }
        }

        /// <summary>
        /// 保存光栅序列
        /// </summary>
        /// <param name="isOneRowAndOneCol"> 单行/单列 </param>
        /// <returns></returns>
        public void SavePattern(bool isOneRowAndOneCol, out string savePath)
        {
            DateTime now = DateTime.Now;
            savePath = $"{PathTool.DataPatternImagePath}{now.ToString("yyyyMMdd_HHmmff")}\\";
            if (!Directory.Exists(savePath))
                Directory.CreateDirectory(savePath);
            //保存序列为文件
            for (int i = 0; i < PatternList.Count; i++)
            {
                Cv2.ImWrite(
                    $"{savePath}pattern_{(i + 1).ToString().PadLeft(2, '0')}.png",
                    isOneRowAndOneCol ? PatternList[i].Image : PatternList[i].GetFullImage);
            }
        }
        #endregion
    }
    /// <summary>
    /// 条纹光栅图像参数
    /// </summary>
    [Serializable]
    public class StripedGratingImageParam
    {
        /// <summary> 格雷码参数 </summary>
        [XmlElement]
        public GrayCodeParam GrayCode = new GrayCodeParam();
        /// <summary> 相移法参数 </summary>
        [XmlElement]
        public PhaseShiftParam PhaseShift = new PhaseShiftParam();
    }
    /// <summary>
    /// 格雷码参数
    /// </summary>
    [Serializable]
    public class GrayCodeParam
    {
        /// <summary> 图像尺寸 </summary>
        public int ImageWidth = 960;
        /// <summary> 图像尺寸 </summary>
        public int ImageHeight = 540;

        /// <summary> 全AllW黑全白 </summary>
        public bool IsAllWhiteAndBlack = true;
        /// <summary> 互补格雷码 </summary>
        public bool IsComplementaryGrayCode = true;
        /// <summary> 自动计算深度 </summary>
        public bool IsAutoDepth = true;

        /// <summary> 垂直向深度 </summary>
        public int VerticalDepth = 10;
        /// <summary> 水平向深度 </summary>
        public int HorizontalDepth = 10;
        /// <summary> 最佳垂直向深度 </summary>
        public int BesttVerticalDepth { get { return StripedGratingImageTool.GetDepth(ImageWidth); } }
        /// <summary> 最佳水平向深度 </summary>
        public int BesttHorizontalDepth { get { return StripedGratingImageTool.GetDepth(ImageHeight); } }
    }
    /// <summary>
    /// 相移法参数
    /// </summary>
    [Serializable]
    public class PhaseShiftParam
    {
        /// <summary> 图像尺寸 </summary>
        public int ImageWidth = 960;
        /// <summary> 图像尺寸 </summary>
        public int ImageHeight = 540;

        /// <summary> 背景灰度补偿值 </summary>
        public int A = 130;
        /// <summary> 调制灰度振幅值 </summary>
        public int B = 90;
        /// <summary> 总相移数量 </summary>
        public int N = 3;
        /// <summary> 单个周期内的像素数量（条纹周期or节距） /// </summary>
        public double T = 32;

        /// <summary> 是否垂直向 </summary>
        public bool IsVertical = true;
    }
}
